/*  ----------------------------------------------------------------------------
 *  Arello Mobile
 *  Fast 3d Engine
 *  ----------------------------------------------------------------------------
 * Except where otherwise noted, this work is licensed under
 * http://creativecommons.org/licenses/by/3.0
 */

#include "MP3File.h"
#include <resource/Manager.h>
#include "PCMConvert.h"

namespace liba
{
namespace noise
{

MP3File::MP3File( const filesystem::String & file_name )
:	m_stream( resource::Manager::create_resource( file_name +".mp3" ) ),
	error( true ),
	m_decoder( 0 ),
	m_filter2( 0 ),
	m_filter1( 0 ),
	m_output( 0 ),
	to_stop( false )
{
	if ( !m_header.read_header( &m_stream ) )
		return;

	int channels( 0 );

	m_filter1 = new CSynthesisFilter(0);	// filter for the 0 channel

	e_channels which_channels;
	if (m_header.mode() != single_channel)
	{
		which_channels = both;
		channels = 2;
		m_filter2 = new CSynthesisFilter(1);
	}
	else
	{
		which_channels = left;
		channels = 1;
		m_filter2 = 0;
	}

	int samples_per_sec = m_header.frequency();
	int bits_per_sample = 16;
/*
#ifdef FIXED_POINT
	m_length = ((size_t)(m_header.total_ms(&m_stream).getInt()/1000)*m_channels*m_samplesPerSec) << 1;
#else
	m_length = ((size_t)(m_header.total_ms(&m_stream)/1000)*m_channels*m_samplesPerSec) << 1;
#endif
*/
	format = Format( samples_per_sec, channels, bits_per_sample );

	m_output = new COutputBuffer( channels );
	m_decoder = new CMP3Decoder( &m_stream, &m_header, m_filter1, m_filter2, which_channels );
	error = false;

	const int delay = 576 + 528 + 1;
//	char delay_place[ delay*4 ];
	decode(delay);
}

MP3File::~MP3File()
{
	delete m_decoder;
	delete m_filter2;
	delete m_filter1;
	delete m_output;
}

int MP3File::get_length()
{
	float seconds = m_header.total_ms(&m_stream)/1000.0f;
	int length = (size_t)(seconds*format.get_frequency());
	return length;
}

int MP3File::get_frequency()
{
	return format.get_frequency();
}

bool MP3File::has_native(Type channel)
{
	return channel == DataSource::STEREO_LEFT || channel == DataSource::STEREO_RIGHT;
}

int MP3File::decode(int samples)
{
	if( error )
		return 0;

	if( samples == 0 )
		return 0;

	int required_size = samples * format.get_sample_size();
	decoded_data.resize( 0 );
	decoded_data.resize( required_size, 0 );

	required_size >>= 1; // number of elements size == 2 bytes
	int old_bytes = m_output->init( &(decoded_data.front()), required_size );
	if ( old_bytes == -1 )
	{
		samples_decoded = samples;
		return samples;
	}

	if ( to_stop )
	{
		samples_decoded = ( old_bytes << 1 )/format.get_sample_size();
		return samples_decoded;
	}

	do
	{
		m_decoder->decode( *m_output );	// decode one frame
		if ( !m_header.read_header( &m_stream ) )
		{
			to_stop = true;
			break;
		}
	}
	while (m_output->size() < required_size);

	samples_decoded = ( m_output->size() << 1) / format.get_sample_size();	// return number of the bytes (!!!)
	return samples_decoded;
}

void MP3File::get_data(Type channel, void * dst, int dst_stride, int dst_bits)
{
	int chan = 0;

	switch( channel )
	{
	case DataSource::MONO:
	case DataSource::STEREO_LEFT:
	case DataSource::SURROUND_FRONTLEFT:
	case DataSource::SURROUND_REARLEFT:
	case DataSource::SURROUND_SUBWOOFER:
	case DataSource::SURROUND_CENTER:
		chan = 0;
		break;
	case DataSource::STEREO_RIGHT:
	case DataSource::SURROUND_FRONTRIGHT:
	case DataSource::SURROUND_REARRIGHT:
		chan = 1;
		break;
	}
	int src_bits = format.get_bit_rate();
	int src_bytes = (src_bits + 7) / 8;
	int src_stride = format.get_sample_size();
	return pcm_bit_convert( chan * src_bytes + &(decoded_data.front()), src_stride, src_bits, src_bytes,
		dst, dst_stride, dst_bits, (dst_bits + 7) / 8, samples_decoded );
}

} // namespace noise
} // namesapce liba

